﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

namespace Pixysoft.Framework.Noebe.MemoryCaches.Core
{
    class CacheAdvNoebeCommand : ICacheNoebeCommand
    {
        ICacheSetting setting;

        INoebeCommand command = null;

        public CacheAdvNoebeCommand(ICacheSetting setting)
        {
            this.setting = setting;

            INoebeManager manager = SettingHelper.GetManager(setting);

            this.command = manager.NoebeCommand;
        }



        public bool IsOpen
        {
            get { return command.IsOpen; }
        }

        public void Begin()
        {
            command.Begin();
        }

        public void Begin(System.Data.IsolationLevel level)
        {
            command.Begin(level);
        }
      
        /// <summary>
        /// 使用安全区域操作事务
        /// </summary>
        public void Begin(NoebeTransactionArea transactionArea)
        {
            Begin(IsolationLevel.ReadCommitted, transactionArea);
        }

        /// <summary>
        /// 使用事务安全区操作
        /// </summary>
        /// <param name="level"></param>
        /// <param name="area"></param>
        public void Begin(IsolationLevel level, NoebeTransactionArea transactionArea)
        {
            Begin(level);

            try
            {
                transactionArea();

                if (this.IsOpen)
                {
                    Commit();
                }
            }
            catch (Exception ex)
            {
                Rollback();

                throw ex;
            }
        }

        public void Commit()
        {
            command.Commit();
        }

        public void Rollback()
        {
            command.Rollback();
        }



        public int Insert(System.Data.DataTable table)
        {
            CacheDataSet.Instance.ClearTable(table.TableName);

            return command.Insert(table);
        }

        public int Insert(System.Data.DataRow row)
        {
            CacheDataSet.Instance.ClearTable(row.Table.TableName);

            return command.Insert(row);
        }

        public int AutoInsert(System.Data.DataTable table)
        {
            CacheDataSet.Instance.ClearTable(table.TableName);

            return command.AutoInsert(table);
        }

        public int AutoInsert(System.Data.DataRow row)
        {
            CacheDataSet.Instance.ClearTable(row.Table.TableName);

            return command.AutoInsert(row);
        }

        public int Update(System.Data.DataTable table)
        {
            CacheDataSet.Instance.ClearTable(table.TableName);

            return command.Update(table);
        }

        public int Update(System.Data.DataRow row)
        {
            CacheDataSet.Instance.ClearTable(row.Table.TableName);

            return command.Update(row);
        }

        public int Delete(System.Data.DataTable table)
        {
            CacheDataSet.Instance.ClearTable(table.TableName);

            return command.Delete(table);
        }

        public int Delete(System.Data.DataRow row)
        {
            CacheDataSet.Instance.ClearTable(row.Table.TableName);

            return command.Delete(row);
        }



        public void Dispose()
        {
            command.Dispose();
        }


        public NoebeParameterCollection Parameters
        {
            get { return command.Parameters; }
        }

        public System.Data.DataTable ExecuteReader()
        {
            //获取需要的参数

            string sqlValue = command.SQL.Trim().ToUpper();

            string cachekey = StringHelper.CreateCacheKey(command);

            //获取缓存

            if (CacheDataSet.Instance.Contains(setting.SettingName, sqlValue, cachekey))
            {
                return ExecuteReaderFromCache(sqlValue, cachekey);
            }

            return ExecuteReaderToCache(sqlValue, cachekey);
        }

        private DataTable ExecuteReaderToCache(string sqlValue, string cachekey)
        {
            //读取数据库

            DataTable table = command.ExecuteReader();


            //获取缓存的表结构

            DataTable schema = table.Clone();


            //创建缓存

            ICacheDataTable cachetable = null;

            if (CacheDataSet.Instance.Contains(setting.SettingName, sqlValue))
            {
                cachetable = CacheDataSet.Instance.GetCache(setting.SettingName, sqlValue);

                //判断表结构是否一致 

                if (!cachetable.SchemaIsConfirmed(schema))
                {
                    CacheDataSet.Instance.RemoveTable(schema.TableName);

                    cachetable = CacheDataSet.Instance.CreateCache(setting.SettingName, sqlValue, schema, setting.MaxCapacity);
                }
            }
            else
            {
                cachetable = CacheDataSet.Instance.CreateCache(setting.SettingName, sqlValue, schema, setting.MaxCapacity);
            }

            if (table.Rows.Count == 0)
            {
                //10-04-10 null使用默认时间限制

                cachetable.AddNullCache(cachekey, StringHelper.default_cache_nulltimespan);

                //cachetable.AddNullCache(cachekey, setting.TimeSpan);
            }
            else
            {
                switch (setting.CacheMode)
                {
                    case CacheMode.DeepCache:
                        {
                            cachetable.AddDeepCache(cachekey, setting.TimeSpan, table);

                            break;
                        }
                    case CacheMode.ShallowCache:
                    default:
                        {
                            cachetable.AddShallowCache(cachekey, setting.TimeSpan, table);

                            break;
                        }
                }
            }

            return table;
        }

        private DataTable ExecuteReaderFromCache(string sqlValue, string cachekey)
        {
            ICacheDataTable cachetable = CacheDataSet.Instance.GetCache(setting.SettingName, sqlValue);

            DataTable rtable = cachetable.Get(cachekey);

            AsyncExecuteReaderToCacheParameter paramter = new AsyncExecuteReaderToCacheParameter();
            paramter.CacheKey = cachekey;
            paramter.SqlValue = sqlValue;

            if (cachetable.IsOverdued(cachekey))
                AsyncExecuteReaderToCache(paramter);//2010-09-26 不使用异步
            //Pixysoft.MultiThread.AsyncInvoker<AsyncExecuteReaderToCacheParameter>.Invoke(AsyncExecuteReaderToCache, paramter);

            return rtable;
        }

        private void AsyncExecuteReaderToCache(AsyncExecuteReaderToCacheParameter parameter)
        {
            string sqlValue = parameter.SqlValue;

            string cachekey = parameter.CacheKey;

            try
            {
                ExecuteReaderToCache(sqlValue, cachekey);
            }
            catch (System.Net.WebException ex) //10-02-11 提高exception的精度
            {
                LoggerHelper.Info("async read data fail.");

                LoggerHelper.Info(ex.Message);
            }
            catch (System.InvalidOperationException ex)// 读取的回复档案出错也会抛异常。
            {
                LoggerHelper.Info("async read data fail.");

                LoggerHelper.Info(ex.Message);
            }
            catch (System.Threading.ThreadAbortException ex)
            {
                LoggerHelper.Info("async read data fail.");

                LoggerHelper.Info(ex.Message);
            }
            catch (Exception ex)
            {
                LoggerHelper.Fatal("sqlvalue={0}, cachekey={1},command.para.count={2}",
                    sqlValue, cachekey, command.Parameters.ValueList.Count);

                if (command.Parameters.ValueList.Count > 0)
                {
                    LoggerHelper.Fatal("current parameter count={0}", command.Parameters.ValueList[0].Count);
                }
                else
                {
                    LoggerHelper.Fatal("current parameter count is zero.");
                }

                LoggerHelper.Fatal(ex);
            }
        }

        class AsyncExecuteReaderToCacheParameter
        {
            string sqlValue;
            string cacheKey;
            public string SqlValue
            {
                get { return sqlValue; }
                set { sqlValue = value; }
            }
            public string CacheKey
            {
                get { return cacheKey; }
                set { cacheKey = value; }
            }
        }



        public string SQL
        {
            get
            {
                return command.SQL;
            }
            set
            {
                command.SQL = value;
            }
        }

        public int Execute()
        {
            string tablename = Pixysoft.SqlCoder.Instance.GetSqlTableName(command.SQL);

            CacheDataSet.Instance.ClearTable(tablename);

            return command.Execute();
        }



        public DataTable Select(string tableName)
        {
            throw new Exception("cache do not support this method.");
        }

        public DataTable SelectByPk(string tableName, params object[] values)
        {
            throw new Exception("cache do not support this method.");
        }



        public void CascadeDelete(System.Data.DataTable table)
        {
            command.CascadeDelete(table);

            CacheDataSet.Instance.ClearTable(table.TableName);
        }

        public void CascadeDelete(System.Data.DataRow row)
        {
            command.CascadeDelete(row);

            CacheDataSet.Instance.ClearTable(row.Table.TableName);
        }



        public void ClearCache()
        {
            string sqlValue = command.SQL.Trim().ToUpper();

            string cachekey = StringHelper.CreateCacheKey(command);

            CacheDataSet.Instance.Clear(setting.SettingName, sqlValue, cachekey);
        }
    }
}
